package com.lap.framework.junit.application.builder;

import com.lap.framework.junit.configure.EnvConfigure;
import com.lap.framework.junit.domain.ClassInfo;
import com.lap.framework.junit.shared.tool.AnnotationUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.File;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class ClassParser {

  private final MethodParser methodParser;

  public ClassParser() {
    this(new MethodParser());
  }

  public ClassParser(MethodParser methodParser) {
    this.methodParser = methodParser;
  }

  public Optional<ClassInfo> parseClass(EnvConfigure env, String basePackage, File file) {
    return parseClassSafely(basePackage, file)
        .map(clazz -> buildClassInfo(env, basePackage, file, clazz));
  }

  private Optional<Class<?>> parseClassSafely(String packageName, File file) {
    String className =
        StringUtils.join(packageName, ".", FilenameUtils.getBaseName(file.getName()));
    try {
      return Optional.of(Class.forName(className));
    } catch (ClassNotFoundException e) {
      log.warn("Class not found: {}", className);
      return Optional.empty();
    } catch (Exception e) {
      log.error("Error loading class: {}", className, e);
      return Optional.empty();
    }
  }

  private ClassInfo buildClassInfo(
      EnvConfigure configure, String basePackage, File file, Class<?> clazz) {
    ClassInfo classInfo = new ClassInfo();
    classInfo.setFileName(FilenameUtils.getBaseName(file.getName()));
    classInfo.setPackageName(basePackage);
    classInfo.setSupperClass(configure.getSupperClass());
    classInfo.setRemark(getClassRemark(clazz));
    classInfo.setMethods(methodParser.parseMethods(clazz, getParentUri(clazz)));
    return classInfo;
  }

  private String getClassRemark(Class<?> clazz) {
    return Optional.ofNullable(clazz.getAnnotation(Tag.class)).map(Tag::name).orElse("");
  }

  private String getParentUri(Class<?> clazz) {
    return Optional.ofNullable(AnnotationUtil.getRequestMappingUri(clazz))
        .orElseGet(() -> AnnotationUtil.getRequestMappingUri(clazz.getSuperclass()));
  }
}
